/**
 * @brief 2.3.2 指针
 * 与引用类似，指针也实现了对其他对象的间接访问。然而指针与引用相比又有很多不同点。
 * 其一，指针本身就是一个对象，允许对指针赋值和拷贝，而且在指针的生命周期内它可以先后指向几个不同的对象。
 * 其二，指针无须在定义时赋初值。和其他内置类型一样，在块作用域内定义的指针如果没有被初始化，也将拥有一个不确定的值。
 * 
 * 指针存放某个对象的地址，要想获取该地址，需要使用取地址符（操作符&）
 * 如果指针指向了一个对象，则允许使用解引用符（操作符＊）来访问该对象
 * @param argc 
 * @param argv 
 * @return int 
 */
#include <iostream>

int main(int argc, char const *argv[])
{
    int *ip1, *ip2; // ip1和ip2都是指向int型对象的指针
    double dp, *dp2;// dp2是指向double型对象的指针，dp是double型对象

    // 指针存放某个对象的地址，要想获取该地址，需要使用取地址符（操作符&）
    int iVal = 42;
    int *p = &iVal; // p存放变量iVal的地址，或者说p是指向变量iVal的指针

    int &refVal = iVal;
    int *p1 = &refVal; // 指针p1指向变量iVal，因为引用不是对象，没有实际地址，所以不能定义指向引用的指针
    // int *p1 = refVal; // 错误

    double dval;
    double *pd = &dval;
    double *pd2 = pd; // 正确，pd2的初始值是指向double
    // int *pi = pd; // 错误，指针pi的类型和pd的类型不符
    // pi = &dVal; // 错误，试图把double型对象的地址赋给int型指针

    // 如果指针指向了一个对象，则允许使用解引用符（操作符＊）来访问该对象
    std::cout << *p << std::endl; // 打印42

    *p = 0; // 给解引用的结果赋值，实际上就是给指针所指的对象赋值
    std::cout << *p << std::endl; // 打印0
    std::cout << iVal << std::endl; // 打印0

    // 空指针不指向任何对象，以下列出几个生成空指针的方法：
    int *np1 = nullptr;
    int *np2 = 0; // 将np2初始化为字面常量0
    int *np3 = NULL;

    // 对于两个类型相同的合法指针，可以用相等操作符（==）或不相等操作符（！=）来比较它们
    // 比较的结果是布尔类型。如果两个指针存放的地址值相同，则它们相等；反之它们不相等。
    if (pd == pd2)
    {
        std::cout << std::string("The two pointers are stored at equal addresses.") << std::endl;
    }

    // void * 指针，void＊是一种特殊的指针类型，可用于存放任意对象的地址。
    double obj = 3.14;
    int *pobj = &iVal;
    void *pv = &obj; // void * 指针，指向任意类型的对象
    pv = pobj; // void * 指针，可以存放任意类型的指针
    // std::cout << *pv << std::endl; // 不能解引用void * 指针，因为我们不知道它指向的对象到底是什么类型



    return 0;
}
